----------------------------------------------------------------------------------
-- Project 2, Part 2
-- Jason Parrott
-- Rodney McGee
--
-- Main loop
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity program is
    Port ( clk : in  STD_LOGIC;
           reset : in  STD_LOGIC;
           keyboard_ascii : in  STD_LOGIC_VECTOR (7 downto 0);
           keyboard_ready : in  STD_LOGIC;
           ascii_code : out  STD_LOGIC_VECTOR (7 downto 0);
           lcd_data_ready : out  STD_LOGIC;
           encrypt_decrypt : in  STD_LOGIC);
end program;

architecture Behavioral of program is

component user_auth  
    port (
				clk : in std_logic;
				reset : in std_logic;
				password_ready : in std_logic;
				password : in std_logic_vector(127 downto 0);
				authenticated : out std_logic;
				authenticated_ready : out std_logic);
end component;

component crypto_top
    Port ( clk     : in  STD_LOGIC;
           reset   : in  STD_LOGIC;
			  ed      : in STD_LOGIC;
		  	  i_ready : in STD_LOGIC;
			  din     : in STD_LOGIC_VECTOR (127 downto 0);
			  dout    : out STD_LOGIC_VECTOR (127 downto 0);
			  o_ready : out  STD_LOGIC);
end component;

signal wait_counter : STD_LOGIC_VECTOR (26 downto 0);  --2 sec - 101111101011110000100000000
signal shortcounter : STD_LOGIC_VECTOR (24 downto 0);  --.5 sec - 1011111010111100001000000
signal tinycounter : STD_LOGIC_VECTOR (10 downto 0);
signal allow_keyboard : STD_LOGIC;

signal welcome_message : STD_LOGIC_VECTOR (127 downto 0);
signal password_message : STD_LOGIC_VECTOR (127 downto 0);
signal accessgranted_message : STD_LOGIC_VECTOR (127 downto 0);
signal accessdenied_message : STD_LOGIC_VECTOR (127 downto 0);
signal encode_message : STD_LOGIC_VECTOR (127 downto 0);
signal decode_message : STD_LOGIC_VECTOR (127 downto 0);
signal message_buffer : STD_LOGIC_VECTOR (127 downto 0);

signal string_buffer : STD_LOGIC_VECTOR (127 downto 0);

signal ua_password : STD_LOGIC_VECTOR (127 downto 0);
signal ua_password_ready : STD_LOGIC;
signal ua_authenticated : STD_LOGIC;
signal ua_authenticated_ready : STD_LOGIC;

signal crypto_input : STD_LOGIC_VECTOR (127 downto 0);
signal crypto_output : STD_LOGIC_VECTOR (127 downto 0);
signal crypto_input_ready : STD_LOGIC;
signal crypto_output_ready : STD_LOGIC;

type statetype is (	S1, S11, S12, S13, S14, S15, S16,                -- Print out welcome message
							S2, S21, S22, S23, S24, S25, S26,                -- Password Prompt
							S3, S301, S31, S32, S33, S34, S35,               -- Check Authentication
							S4, S41, S42, S43, S44, S45, S46, S47, S48,      -- Encrypt/Decrypt Prompt
							S5, S51, S52, S53, S54, S55, S56, S57, S58, S59, -- Display crypto result
							S6, S61,                                         -- Pause for keyboard
							S99);                              
signal state, next_state : statetype := S1;

begin

-- Port mappings for addtional modules needed within the program loop
UA : user_auth port map (clk, reset, ua_password_ready, ua_password, ua_authenticated, ua_authenticated_ready);
CR : crypto_top port map (clk, reset, encrypt_decrypt, crypto_input_ready, crypto_input, crypto_output, crypto_output_ready);

state <= next_state;

process (clk, reset) is
variable string_counter: std_logic_vector (7 downto 0) := "00000000";
variable line_counter: std_logic_vector (5 downto 0) := "000000";
begin
		 if (reset = '1') then
					next_state <= S1;
					string_counter := "00000000";
					lcd_data_ready <= '0';
					
					allow_keyboard <= '0';
					line_counter := "000000";
					
					wait_counter <= "000000000000000000000000000";
					shortcounter <= "0000000000000000000000000";
					tinycounter <= "00000000000";
					
					welcome_message       <= x"57656c636f6d65204350454734323221";
					password_message      <= x"456e7465722050617373776f72643a20";
					accessgranted_message <= x"416363657373204772616e7465642120";
					accessdenied_message  <= x"4163636573732044656e696564203d28";
					decode_message        <= x"5465787420746f206465636f64653a20";
					encode_message        <= x"5465787420746f20656e636f64653a20";
					
					string_buffer			 <= x"20202020202020202020202020202020";
					
					ua_password				 <= x"00000000000000000000000000000000";
					ua_password_ready 	 <= '0';
					
					crypto_input			 <= x"00000000000000000000000000000000";
					crypto_input_ready 	 <= '0';
		 elsif (rising_edge(clk)) then
					case state is
					
					-- ===============================================
					-- State 1: Print Welcome Message
					-- ===============================================
					
					-- Wait .5 seconds
					when S1 =>
						if(shortcounter = "1011111010111100001000000") then
							shortcounter <= "0000000000000000000000000";
							next_state <= S11;
						else
							shortcounter <= shortcounter + 1;
							next_state <= S1;
						end if;
							  
					-- Print out welcome message
					when S11 =>
						ascii_code <= welcome_message(127 downto 120);
						lcd_data_ready <= '1';
						
						welcome_message <= welcome_message(119 downto 0) & "00000000";
						string_counter := string_counter + 8;
						next_state <= S12;
						
					when S12 =>
						if(tinycounter = "11111111111") then
							tinycounter <= "00000000000";
							lcd_data_ready <= '0';
							if(string_counter = x"80") then
								string_counter := "00000000";
								next_state <= S13;
							else
								next_state <= S11;
							end if;
						else
							tinycounter <= tinycounter + 1;
							next_state <= S12;
						end if;
							  
					-- Wait 2 seconds
					when S13 =>
						if(wait_counter = "101111101011110000100000000") then
							wait_counter <= "000000000000000000000000000";
							next_state <= S14;
						else
							wait_counter <= wait_counter + 1;
							next_state <= S13;
						end if;
						
					-- Erase screen
					when S14 =>
						ascii_code <= x"0D";
						lcd_data_ready <= '1';
						next_state <= S15;
						
					when S15 =>
						if(tinycounter = "11111111111") then
							tinycounter <= "00000000000";
							lcd_data_ready <= '0';
							next_state <= S16;
						else
							tinycounter <= tinycounter + 1;
							next_state <= S15;
						end if;
						
					-- Wait .5 seconds
					when S16 =>
						if(shortcounter = "1011111010111100001000000") then
							shortcounter <= "0000000000000000000000000";
							next_state <= S2;
						else
							shortcounter <= shortcounter + 1;
							next_state <= S16;
						end if;
						
					-- ===============================================
					-- State 2: Password Prompt
					-- ===============================================

					-- Print out ENTER PASSWORD Prompt
					when S2 =>
						ascii_code <= password_message(127 downto 120);
						lcd_data_ready <= '1';
						
						password_message <= password_message(119 downto 0) & "00000000";
						string_counter := string_counter + 8;
						next_state <= S21;
						
					when S21 =>
						if(tinycounter = "11111111111") then
							tinycounter <= "00000000000";
							lcd_data_ready <= '0';
							if(string_counter = x"80") then
								string_counter := "00000000";
								allow_keyboard <= '1';
								next_state <= S22;
							else
								next_state <= S2;
							end if;
						else
							tinycounter <= tinycounter + 1;
							next_state <= S21;
						end if;
							  
					-- Process keyboard input
					when S22 =>
						if (keyboard_ready = '1') then
							if (keyboard_ascii = x"0D") then -- Enter/Return
								line_counter := "000000";
								next_state <= S24;
							elsif(keyboard_ascii = x"08") then  -- Backspace
								if line_counter /= "000000" then
									ua_password <= x"00" & ua_password(127 downto 8);
									line_counter := line_counter - 1;
									
									ascii_code <= keyboard_ascii; -- Put backspace to lcd to 'backup' cursor
									lcd_data_ready <= '1';
									next_state <= S23;
								else	
									next_state <= S22;
								end if;
							elsif(keyboard_ascii = x"00") then  -- Unknown
								next_state <= S22;
							elsif (allow_keyboard = '1') then
								if line_counter /= "010000" then
									ua_password <= ua_password(119 downto 0) & keyboard_ascii;
									
									ascii_code <= x"2A";  --keyboard_ascii;
									line_counter := line_counter + 1;
									lcd_data_ready <= '1';
									next_state <= S23;
								else
									next_state <= S22;
								end if;
							else
								line_counter := "000000";
								next_state <= S24;
							end if;
						else
							lcd_data_ready <= '0';
							next_state <= S22;
						end if;
							  
					when S23 =>
						lcd_data_ready <= '0';
						if line_counter = "010000" then
							line_counter := "000000";
							allow_keyboard <= '0';
							next_state <= S24;
						else
							next_State <= S22;
						end if;
						
					-- Erase screen
					when S24 =>
						ascii_code <= x"0D";
						lcd_data_ready <= '1';
						next_state <= S25;
						
					when S25 =>
						if(tinycounter = "11111111111") then
							tinycounter <= "00000000000";
							lcd_data_ready <= '0';
							next_state <= S26;
						else
							tinycounter <= tinycounter + 1;
							next_state <= S25;
						end if;
						
					-- Wait .5 seconds
					when S26 =>
						if(shortcounter = "1011111010111100001000000") then
							shortcounter <= "0000000000000000000000000";
							next_state <= S3;
						else
							shortcounter <= shortcounter + 1;
							next_state <= S26;
						end if;
						
						
					-- ===============================================
					-- State 3: Check Authentication
					-- ===============================================
					
					when S3 =>
						ua_password_ready <= '1';
						next_state <= S301;
					
					when S301 =>
						if(ua_authenticated_ready = '1') then
							if(ua_authenticated = '1') then
								next_state <= S32;
							else
								next_state <= S34;
							end if;
						else
							next_state <= S301;
						end if;
						
					-- Wait .5 seconds
					when S31 =>
						if(shortcounter = "1011111010111100001000000") then
							shortcounter <= "0000000000000000000000000";
							next_state <= S32;
						else
							shortcounter <= shortcounter + 1;
							next_state <= S31;
						end if;
						
					-- Print out ACCESS GRANTED
					when S32 =>
						ascii_code <= accessgranted_message(127 downto 120);
						lcd_data_ready <= '1';
						
						accessgranted_message <= accessgranted_message(119 downto 0) & "00000000";
						string_counter := string_counter + 8;
						next_state <= S33;
						
					when S33 =>
						if(tinycounter = "11111111111") then
							tinycounter <= "00000000000";
							lcd_data_ready <= '0';
							if(string_counter = x"80") then
								string_counter := "00000000";
								next_state <= S4;
							else
								next_state <= S32;
							end if;
						else
							tinycounter <= tinycounter + 1;
							next_state <= S33;
						end if;
					
					-- Print out ACCESS DENIED
					when S34 =>
						ascii_code <= accessdenied_message(127 downto 120);
						lcd_data_ready <= '1';
						
						accessdenied_message <= accessdenied_message(119 downto 0) & "00000000";
						string_counter := string_counter + 8;
						next_state <= S35;
						
					when S35 =>
						if(tinycounter = "11111111111") then
							tinycounter <= "00000000000";
							lcd_data_ready <= '0';
							if(string_counter = x"80") then
								string_counter := "00000000";
								next_state <= S99;
							else
								next_state <= S34;
							end if;
						else
							tinycounter <= tinycounter + 1;
							next_state <= S35;
						end if;
						
					-- ===============================================
					-- State 4: Encode or Decode Input
					-- ===============================================
													  
					-- Wait 2 seconds
					when S4 =>
						if(wait_counter = "101111101011110000100000000") then
							wait_counter <= "000000000000000000000000000";
							next_state <= S41;
						else
							wait_counter <= wait_counter + 1;
							next_state <= S4;
						end if;
						
					-- Erase screen
					when S41 =>
						ascii_code <= x"0D";
						lcd_data_ready <= '1';
						next_state <= S42;
						
					when S42 =>
						if(tinycounter = "11111111111") then
							tinycounter <= "00000000000";
							lcd_data_ready <= '0';
							next_state <= S43;
						else
							tinycounter <= tinycounter + 1;
							next_state <= S42;
						end if;
						
					-- Wait .5 seconds
					when S43 =>
						if(shortcounter = "1011111010111100001000000") then
							shortcounter <= "0000000000000000000000000";
							next_state <= S44;
						else
							shortcounter <= shortcounter + 1;
							next_state <= S43;
						end if;
						
					-- Figure out ENCODE or DECODE 
					when S44 =>
					if(encrypt_decrypt = '1') then  -- If switch is high/low
							message_buffer <= encode_message;
						else
							message_buffer <= decode_message;
						end if;
						next_state <= S45;
						
					-- Print out ENCODE/DECODE Prompt
					when S45 =>
						ascii_code <= message_buffer(127 downto 120);
						lcd_data_ready <= '1';
						
						message_buffer <= message_buffer(119 downto 0) & "00000000";
						string_counter := string_counter + 8;
						next_state <= S46;
						
					when S46 =>
						if(tinycounter = "11111111111") then
							tinycounter <= "00000000000";
							lcd_data_ready <= '0';
							if(string_counter = x"80") then
								string_counter := "00000000";
								allow_keyboard <= '1';
								next_state <= S47;
							else
								next_state <= S45;
							end if;
						else
							tinycounter <= tinycounter + 1;
							next_state <= S46;
						end if;
							  
					-- Process keyboard input
					when S47 =>
						if(keyboard_ready = '1' and allow_keyboard = '1') then
							if(keyboard_ascii = x"0D") then    -- Enter/Return
								line_counter := "000000";
								allow_keyboard <= '0';
								next_state <= S5;
							elsif(keyboard_ascii = x"08") then  -- Backspace
								if line_counter /= "000000" then
									string_buffer <= x"00" & string_buffer(127 downto 8);
									line_counter := line_counter - 1;
									
									ascii_code <= keyboard_ascii; -- Put backspace to lcd to 'backup' cursor
									lcd_data_ready <= '1';
									next_state <= S48;
								else
									next_state <= S47;
								end if;
							elsif(keyboard_ascii = x"00") then  -- Unknown
								next_state <= S47;
							else
								if line_counter /= "010000" then
									string_buffer <= string_buffer(119 downto 0) & keyboard_ascii;
									
									ascii_code <= keyboard_ascii;
									line_counter := line_counter + 1;
									lcd_data_ready <= '1';
									next_state <= S48;
								else
									next_state <= S47;
								end if;
							end if;
						else
							lcd_data_ready <= '0';
							next_state <= S47;
						end if;
							  
					when S48 =>
						lcd_data_ready <= '0';
						if line_counter = "010000" then
							line_counter := "000000";
							allow_keyboard <= '0';
							next_state <= S5;
						else
							next_state <= S47;
						end if;
						
						
					-- ===============================================
					-- State 5: Result of encode/decode
					-- ===============================================
											
					-- Send our string to the crypto
					when S5 =>
						crypto_input <= string_buffer;
						crypto_input_ready <= '1';
						next_state <= S51;
						
					-- Erase screen
					when S51 =>
						ascii_code <= x"0D";
						lcd_data_ready <= '1';
						next_state <= S52;
						
					when S52 =>
						if(tinycounter = "11111111111") then
							tinycounter <= "00000000000";
							lcd_data_ready <= '0';
							next_state <= S53;
						else
							tinycounter <= tinycounter + 1;
							next_state <= S52;
						end if;
						
					-- Wait .5 seconds
					when S53 =>
						if(shortcounter = "1011111010111100001000000") then
							shortcounter <= "0000000000000000000000000";
							next_state <= S54;
						else
							shortcounter <= shortcounter + 1;
							next_state <= S53;
						end if;
						
					-- Print out what the user typed
					when S54 =>
						ascii_code <= string_buffer(127 downto 120);
						lcd_data_ready <= '1';
						
						string_buffer <= string_buffer(119 downto 0) & x"20";
						string_counter := string_counter + 8;
						next_state <= S55;
						
					when S55 =>
						if(tinycounter = "11111111111") then
							tinycounter <= "00000000000";
							lcd_data_ready <= '0';
							if(string_counter = x"80") then
								string_counter := "00000000";
								next_state <= S56;
							else
								next_state <= S54;
							end if;
						else
							tinycounter <= tinycounter + 1;
							next_state <= S55;
						end if;
						
					-- Wait for crypto to be finished
					when S56 =>
						if(crypto_output_ready = '1') then
							string_buffer <= crypto_output;
							crypto_input_ready <= '0';
							next_state <= S57;
						else
							next_state <= S56;
						end if;
						
					-- Wait .5 seconds
					when S57 =>
						if(shortcounter = "1011111010111100001000000") then
							shortcounter <= "0000000000000000000000000";
							next_state <= S58;
						else
							shortcounter <= shortcounter + 1;
							next_state <= S57;
						end if;
						
					-- Print out the result of crypto
					when S58 =>
						ascii_code <= string_buffer(127 downto 120);
						lcd_data_ready <= '1';
						
						string_buffer <= string_buffer(119 downto 0) & x"20";
						string_counter := string_counter + 8;
						next_state <= S59;
						
					when S59 =>
						if(tinycounter = "11111111111") then
							tinycounter <= "00000000000";
							lcd_data_ready <= '0';
							if(string_counter = x"80") then
								string_counter := "00000000";
								allow_keyboard <= '1';
								next_state <= S6;
							else
								next_state <= S58;
							end if;
						else
							tinycounter <= tinycounter + 1;
							next_state <= S59;
						end if;
											
					-- ===============================================
					-- State 6: Stay in this state - Wait for keyboard
					-- ===============================================
					when S6 =>
						if (keyboard_ready = '1') then
							if (allow_keyboard = '1') then
								next_state <= S61;
							end if;
						else
							next_state <= S6;
						end if;
							  
					when S61 =>
						allow_keyboard <= '0';
						next_state <= S41;
						
						
					-- ===============================================
					-- State 99: Infinite Loop
					-- ===============================================
					when S99 =>
						next_state <= S99;	
						
						
						
					end case;
		 end if;
end process;


end Behavioral;

